{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Pytorch Convolutional Neural Network"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src='https://miro.medium.com/max/3288/1*uAeANQIOQPqWZnnuH-VEyw.jpeg'   width=\"700\" height=\"350\">"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Importing libraries"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from glob import glob\n",
    "from PIL import Image\n",
    "import pandas as pd\n",
    "from time import time\n",
    "from sklearn.model_selection import train_test_split\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "import torch.optim as optim\n",
    "from torch.utils.data import DataLoader\n",
    "from torch.autograd import Variable\n",
    "from sklearn.preprocessing import LabelEncoder\n",
    "import os\n",
    "os.chdir('f:/exterior')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Loading all file names"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "files = glob('*.jpg')\n",
    "files = np.random.permutation(files)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array(['smart_fortwo_2013_17_15_700_10_3_61_60_106_34_RWD_2_2_Convertible_zHI.jpg',\n",
       "       'Honda_Ridgeline_2008_28_17_240_35_6_76_70_206_15_4WD_5_4_Pickup_Ttk.jpg',\n",
       "       'Audi_A7_2012_59_18_310_30_6_75_55_195_18_AWD_4_4_4dr_naU.jpg',\n",
       "       'Kia_Sedona_2017_33_18_270_33_6_78_69_201_18_FWD_8_4_Van_CVX.jpg',\n",
       "       'Volvo_V60_2016_37_17_250_25_5_73_58_182_20_AWD_5_4_4dr_nok.jpg'],\n",
       "      dtype='<U88')"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "files[:5]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Keeping only Audi/BMW"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "cars = [i for i in files if i.split('_')[0] in ('Audi', 'BMW')]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Audi_A7_2012_59_18_310_30_6_75_55_195_18_AWD_4_4_4dr_naU.jpg',\n",
       " 'BMW_2-Series_2020_41_17_240_20_4_69_55_174_23_RWD_4_2_Convertible_DfX.jpg',\n",
       " 'Audi_A4_2019_37_17_180_20_4_72_56_186_27_FWD_5_4_4dr_CyR.jpg',\n",
       " 'BMW_X5_2017_56_19_300_30_6_76_69_192_18_RWD_5_4_SUV_eXs.jpg',\n",
       " 'Audi_Q8_2019_67_20_330_30_6_78_67_196_17_AWD_5_4_SUV_MmZ.jpg']"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "cars[:5]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "audi = [i for i in cars if i.split('_')[0] == 'Audi']\n",
    "bmw = [i for i in cars if i.split('_')[0] == 'BMW']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "There are 3131 Audis and 4121 BMWs.\n"
     ]
    }
   ],
   "source": [
    "print(f'There are {len(audi)} Audis and {len(bmw)} BMWs.')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "n_each = np.minimum(len(audi), len(bmw))//1000*1000"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "cars = np.random.choice(audi, n_each, replace=False).tolist() +\\\n",
    "       np.random.choice(bmw, n_each, replace=False).tolist()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "makes = np.repeat('Audi', n_each).tolist() + np.repeat('BMW', n_each).tolist()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "cat = ['Audi', 'BMW']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "assert len(makes) == len(cars), 'The X and Y are not of the same length!'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total length is 6,000 cars.\n"
     ]
    }
   ],
   "source": [
    "print('Total length is {:,} cars.'.format(len(cars)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Cropping function"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "def crop(filename, w, h):\n",
    "    image = Image.open(filename)\n",
    "    width = image.size[0]\n",
    "    height = image.size[1]\n",
    "\n",
    "    aspect = width / float(height)\n",
    "\n",
    "    ideal_width = w\n",
    "    ideal_height = h\n",
    "\n",
    "    ideal_aspect = ideal_width / float(ideal_height)\n",
    "\n",
    "    if aspect > ideal_aspect:\n",
    "        new_width = int(ideal_aspect * height)\n",
    "        offset = (width - new_width) / 2\n",
    "        resize = (offset, 0, width - offset, height)\n",
    "    else:\n",
    "        new_height = int(width / ideal_aspect)\n",
    "        offset = (height - new_height) / 2\n",
    "        resize = (0, offset, width, height - offset)\n",
    "\n",
    "    img = image.crop(resize).resize((ideal_width, ideal_height), Image.ANTIALIAS)\n",
    "\n",
    "    return np.array(img)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Loading and cropping images"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Scaling...\n",
      "10.00% elapsed.\n",
      "20.00% elapsed.\n",
      "30.00% elapsed.\n",
      "40.00% elapsed.\n",
      "50.00% elapsed.\n",
      "60.00% elapsed.\n",
      "70.00% elapsed.\n",
      "80.00% elapsed.\n",
      "90.00% elapsed.\n",
      "100.00% elapsed.\n",
      "Done. 49 seconds\n"
     ]
    }
   ],
   "source": [
    "print('Scaling...')\n",
    "start = time()\n",
    "x = []\n",
    "num_to_load = len(cars)\n",
    "for ix, file in enumerate(cars[:num_to_load], 1):\n",
    "    image = crop(file, 75, 45)\n",
    "    x.append(image)\n",
    "    if ix % (num_to_load // 10) == 0:\n",
    "        print(f'{ix/len(cars):.2%} elapsed.')\n",
    "print(f'\\rDone. {int(time() - start)} seconds')\n",
    "makes = makes[:num_to_load]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Turning the pictures into arrays"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "le = LabelEncoder()\n",
    "y = le.fit_transform(makes)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = np.array(x, dtype=np.float32)\n",
    "y = np.array(y, dtype=np.float32)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(6000, 45, 75, 3)\n"
     ]
    }
   ],
   "source": [
    "n, h, w, c = x.shape\n",
    "print(x.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "X and Y shapes are correct! (6000 samples each)\n"
     ]
    }
   ],
   "source": [
    "if x.shape[0] == y.shape[0]:\n",
    "    print('X and Y shapes are correct! (%i samples each)' % x.shape[0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "yy, xx = y.nbytes, x.nbytes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The size of X is 0.24 GB and the size of Y is 0.0240 MB.\n"
     ]
    }
   ],
   "source": [
    "print(f'The size of X is {xx/1e9:,.2f} GB and the size of Y is {yy/1e6:,.4f} MB.')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The size of the data we are using is 6,000 pictures.\n"
     ]
    }
   ],
   "source": [
    "print(f'The size of the data we are using is {x.shape[0]:,} pictures.')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "# files, cars = None, None"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Displaying the pictures"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 864x864 with 4 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "np.random.seed(45)\n",
    "fig = plt.figure(figsize=(12, 12))\n",
    "for i in range(1, 5):\n",
    "    rand = np.random.randint(x.shape[0])\n",
    "    plt.subplot(1, 5, i)\n",
    "    ax = plt.imshow(x[rand].astype(np.uint8), cmap='gray')\n",
    "    plt.title('<{}>'.format(cat[int(y[rand])].capitalize()))\n",
    "    yticks = plt.xticks([])\n",
    "    yticks = plt.yticks([])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Cross-validation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=2e-1, \n",
    "                                                    shuffle=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The size of the training set is 4,800 and the size of the test set is 1,200.\n"
     ]
    }
   ],
   "source": [
    "trainsize, testsize = x_train.shape[0], x_test.shape[0]\n",
    "print(f'The size of the training set is {trainsize:,} and the '\\\n",
    "     f'size of the test set is {testsize:,}.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Scaling, casting the arrays"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Done.     \n"
     ]
    }
   ],
   "source": [
    "print('Scaling...', end='')\n",
    "x_train = x_train.reshape(-1, 3, h, w).astype('float32') / 255 \n",
    "x_test = x_test.reshape(-1, 3, h, w).astype('float32') / 255\n",
    "y_train = y_train.astype('int64')\n",
    "y_test = y_test.astype('int64')\n",
    "print('\\rDone.     ')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "First dimension: 4800 \n",
      "Second dimension: 3 \n",
      "Third dimension: 45 \n",
      "Fourth dimension: 75\n"
     ]
    }
   ],
   "source": [
    "samples, first, second, third = x_train.shape\n",
    "print('First dimension: %i' % samples,\n",
    "     '\\nSecond dimension: %i' % first,\n",
    "     '\\nThird dimension: %i' % second,\n",
    "     '\\nFourth dimension: %i' % third)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Sending the arrays to Cuda"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Tensors successfully flushed to CUDA.\n"
     ]
    }
   ],
   "source": [
    "if torch.cuda.is_available():\n",
    "    x_train = torch.from_numpy(x_train) \n",
    "    x_test = torch.from_numpy(x_test) \n",
    "    y_train = torch.from_numpy(y_train) \n",
    "    y_test = torch.from_numpy(y_test)\n",
    "    print('Tensors successfully flushed to CUDA.')\n",
    "else:\n",
    "    print('CUDA not available!')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Clearning memory"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "# x, y = None, None"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Building the ConvNet"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Initially image size, W = 60 <br>\n",
    "Kernel Size, k = 3 <br>\n",
    "Stride , s = 1 <br>\n",
    "Padding, P = 0 <br>\n",
    "The formula for the number of outputs to the next layer of conv2d is: O = { (W - k + 2*P)/s } + 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [],
   "source": [
    "class ConvNet(nn.Module):\n",
    "    \n",
    "    def __init__(self):\n",
    "        super().__init__()\n",
    "        a = 64 \n",
    "        b = 128 \n",
    "        c = 256 \n",
    "        \n",
    "        d = 1024\n",
    "        e = 2048\n",
    "        \n",
    "        self.conv1 = nn.Conv2d(3, a, 3)\n",
    "        self.conv2 = nn.Conv2d(a, b, 3)\n",
    "        self.conv3 = nn.Conv2d(b, c, 3)\n",
    "        \n",
    "        self.fc1 = nn.Linear(3*7*c, d) \n",
    "        self.fc2 = nn.Linear(d, e)\n",
    "        self.fc3 = nn.Linear(e, 2)\n",
    "    \n",
    "    def forward(self, x):\n",
    "        x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))\n",
    "        x = F.max_pool2d(F.relu(self.conv2(x)), (2, 2))\n",
    "        x = F.max_pool2d(F.relu(self.conv3(x)), (2, 2))\n",
    "        \n",
    "        x = x.view(x.size(0), -1)\n",
    "        x = F.relu(self.fc1(x))\n",
    "        x = F.relu(self.fc2(x))\n",
    "        x = F.dropout(x, 0.5)\n",
    "        x = self.fc3(x)\n",
    "        return x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "net = ConvNet()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [],
   "source": [
    "if torch.cuda.is_available():\n",
    "    net.cuda()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ConvNet(\n",
      "  (conv1): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1))\n",
      "  (conv2): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1))\n",
      "  (conv3): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1))\n",
      "  (fc1): Linear(in_features=5376, out_features=1024, bias=True)\n",
      "  (fc2): Linear(in_features=1024, out_features=2048, bias=True)\n",
      "  (fc3): Linear(in_features=2048, out_features=2, bias=True)\n",
      ")\n"
     ]
    }
   ],
   "source": [
    "print(net)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [],
   "source": [
    "optimizer = optim.Adam(net.parameters(), lr=0.001)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [],
   "source": [
    "loss_function = nn.CrossEntropyLoss()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Instantiating the data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [],
   "source": [
    "class CarTrain():\n",
    "    \n",
    "    def __init__(self):\n",
    "        self.len = x_train.shape[0]\n",
    "        self.x_train = x_train\n",
    "        self.y_train = y_train\n",
    "        \n",
    "    def __getitem__(self, index):\n",
    "        return x_train[index], y_train[index] \n",
    "    \n",
    "    def __len__(self):\n",
    "        return self.len"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [],
   "source": [
    "class CarTest():\n",
    "    \n",
    "    def __init__(self):\n",
    "        self.len = x_test.shape[0]\n",
    "        self.x_test = x_test\n",
    "        self.y_test = y_test\n",
    "        \n",
    "    def __getitem__(self, index):\n",
    "        return x_test[index], y_test[index] \n",
    "    \n",
    "    def __len__(self):\n",
    "        return self.len"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Making instances of the data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [],
   "source": [
    "train = CarTrain()\n",
    "test = CarTest()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Making data iterator"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [],
   "source": [
    "train_loader = DataLoader(dataset=train, batch_size=64, shuffle=True)\n",
    "test_loader = DataLoader(dataset=test, batch_size=64, shuffle=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Training the model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[Epoch: 1/100]  [Training Loss: 0.705]  [Test Loss: 0.692]  [Test Accuracy: 0.503]\n",
      "[Epoch: 2/100]  [Training Loss: 0.694]  [Test Loss: 0.693]  [Test Accuracy: 0.510]\n",
      "[Epoch: 3/100]  [Training Loss: 0.693]  [Test Loss: 0.693]  [Test Accuracy: 0.508]\n",
      "[Epoch: 4/100]  [Training Loss: 0.693]  [Test Loss: 0.693]  [Test Accuracy: 0.486]\n",
      "[Epoch: 5/100]  [Training Loss: 0.693]  [Test Loss: 0.694]  [Test Accuracy: 0.502]\n",
      "[Epoch: 6/100]  [Training Loss: 0.694]  [Test Loss: 0.693]  [Test Accuracy: 0.492]\n",
      "[Epoch: 7/100]  [Training Loss: 0.693]  [Test Loss: 0.693]  [Test Accuracy: 0.516]\n",
      "[Epoch: 8/100]  [Training Loss: 0.692]  [Test Loss: 0.693]  [Test Accuracy: 0.508]\n",
      "[Epoch: 9/100]  [Training Loss: 0.688]  [Test Loss: 0.690]  [Test Accuracy: 0.524]\n",
      "[Epoch: 10/100]  [Training Loss: 0.691]  [Test Loss: 0.693]  [Test Accuracy: 0.509]\n",
      "[Epoch: 11/100]  [Training Loss: 0.688]  [Test Loss: 0.690]  [Test Accuracy: 0.508]\n",
      "[Epoch: 12/100]  [Training Loss: 0.688]  [Test Loss: 0.690]  [Test Accuracy: 0.517]\n",
      "[Epoch: 13/100]  [Training Loss: 0.684]  [Test Loss: 0.687]  [Test Accuracy: 0.547]\n",
      "[Epoch: 14/100]  [Training Loss: 0.682]  [Test Loss: 0.685]  [Test Accuracy: 0.532]\n",
      "[Epoch: 15/100]  [Training Loss: 0.676]  [Test Loss: 0.672]  [Test Accuracy: 0.576]\n",
      "[Epoch: 16/100]  [Training Loss: 0.674]  [Test Loss: 0.691]  [Test Accuracy: 0.542]\n",
      "[Epoch: 17/100]  [Training Loss: 0.670]  [Test Loss: 0.662]  [Test Accuracy: 0.586]\n",
      "[Epoch: 18/100]  [Training Loss: 0.657]  [Test Loss: 0.663]  [Test Accuracy: 0.596]\n",
      "[Epoch: 19/100]  [Training Loss: 0.639]  [Test Loss: 0.653]  [Test Accuracy: 0.601]\n",
      "[Epoch: 20/100]  [Training Loss: 0.611]  [Test Loss: 0.655]  [Test Accuracy: 0.624]\n",
      "[Epoch: 21/100]  [Training Loss: 0.599]  [Test Loss: 0.658]  [Test Accuracy: 0.622]\n",
      "[Epoch: 22/100]  [Training Loss: 0.578]  [Test Loss: 0.628]  [Test Accuracy: 0.627]\n",
      "[Epoch: 23/100]  [Training Loss: 0.546]  [Test Loss: 0.613]  [Test Accuracy: 0.652]\n",
      "[Epoch: 24/100]  [Training Loss: 0.511]  [Test Loss: 0.651]  [Test Accuracy: 0.653]\n",
      "[Epoch: 25/100]  [Training Loss: 0.483]  [Test Loss: 0.653]  [Test Accuracy: 0.654]\n",
      "[Epoch: 26/100]  [Training Loss: 0.448]  [Test Loss: 0.757]  [Test Accuracy: 0.666]\n",
      "[Epoch: 27/100]  [Training Loss: 0.411]  [Test Loss: 0.744]  [Test Accuracy: 0.670]\n",
      "[Epoch: 28/100]  [Training Loss: 0.386]  [Test Loss: 0.817]  [Test Accuracy: 0.668]\n",
      "[Epoch: 29/100]  [Training Loss: 0.388]  [Test Loss: 0.838]  [Test Accuracy: 0.682]\n",
      "[Epoch: 30/100]  [Training Loss: 0.341]  [Test Loss: 0.942]  [Test Accuracy: 0.681]\n",
      "[Epoch: 31/100]  [Training Loss: 0.293]  [Test Loss: 0.963]  [Test Accuracy: 0.665]\n",
      "[Epoch: 32/100]  [Training Loss: 0.275]  [Test Loss: 1.039]  [Test Accuracy: 0.686]\n",
      "[Epoch: 33/100]  [Training Loss: 0.269]  [Test Loss: 1.049]  [Test Accuracy: 0.670]\n",
      "[Epoch: 34/100]  [Training Loss: 0.242]  [Test Loss: 1.092]  [Test Accuracy: 0.683]\n",
      "[Epoch: 35/100]  [Training Loss: 0.222]  [Test Loss: 1.187]  [Test Accuracy: 0.695]\n",
      "[Epoch: 36/100]  [Training Loss: 0.212]  [Test Loss: 1.150]  [Test Accuracy: 0.710]\n",
      "[Epoch: 37/100]  [Training Loss: 0.185]  [Test Loss: 1.355]  [Test Accuracy: 0.707]\n",
      "[Epoch: 38/100]  [Training Loss: 0.162]  [Test Loss: 1.402]  [Test Accuracy: 0.693]\n",
      "[Epoch: 39/100]  [Training Loss: 0.153]  [Test Loss: 1.562]  [Test Accuracy: 0.695]\n",
      "[Epoch: 40/100]  [Training Loss: 0.186]  [Test Loss: 1.374]  [Test Accuracy: 0.699]\n",
      "[Epoch: 41/100]  [Training Loss: 0.136]  [Test Loss: 1.452]  [Test Accuracy: 0.710]\n",
      "[Epoch: 42/100]  [Training Loss: 0.096]  [Test Loss: 1.688]  [Test Accuracy: 0.727]\n",
      "[Epoch: 43/100]  [Training Loss: 0.097]  [Test Loss: 1.740]  [Test Accuracy: 0.711]\n",
      "[Epoch: 44/100]  [Training Loss: 0.099]  [Test Loss: 1.791]  [Test Accuracy: 0.715]\n",
      "[Epoch: 45/100]  [Training Loss: 0.109]  [Test Loss: 1.839]  [Test Accuracy: 0.715]\n",
      "[Epoch: 46/100]  [Training Loss: 0.102]  [Test Loss: 1.968]  [Test Accuracy: 0.724]\n",
      "[Epoch: 47/100]  [Training Loss: 0.088]  [Test Loss: 1.980]  [Test Accuracy: 0.724]\n",
      "[Epoch: 48/100]  [Training Loss: 0.072]  [Test Loss: 2.161]  [Test Accuracy: 0.735]\n",
      "[Epoch: 49/100]  [Training Loss: 0.052]  [Test Loss: 2.196]  [Test Accuracy: 0.733]\n",
      "[Epoch: 50/100]  [Training Loss: 0.076]  [Test Loss: 2.102]  [Test Accuracy: 0.719]\n",
      "[Epoch: 51/100]  [Training Loss: 0.075]  [Test Loss: 2.157]  [Test Accuracy: 0.712]\n",
      "[Epoch: 52/100]  [Training Loss: 0.078]  [Test Loss: 2.196]  [Test Accuracy: 0.724]\n",
      "[Epoch: 53/100]  [Training Loss: 0.075]  [Test Loss: 2.195]  [Test Accuracy: 0.726]\n",
      "[Epoch: 54/100]  [Training Loss: 0.104]  [Test Loss: 1.953]  [Test Accuracy: 0.725]\n",
      "[Epoch: 55/100]  [Training Loss: 0.074]  [Test Loss: 2.087]  [Test Accuracy: 0.731]\n",
      "[Epoch: 56/100]  [Training Loss: 0.044]  [Test Loss: 2.232]  [Test Accuracy: 0.731]\n",
      "[Epoch: 57/100]  [Training Loss: 0.028]  [Test Loss: 2.331]  [Test Accuracy: 0.739]\n",
      "[Epoch: 58/100]  [Training Loss: 0.025]  [Test Loss: 2.519]  [Test Accuracy: 0.742]\n",
      "[Epoch: 59/100]  [Training Loss: 0.016]  [Test Loss: 2.701]  [Test Accuracy: 0.735]\n",
      "[Epoch: 60/100]  [Training Loss: 0.014]  [Test Loss: 2.833]  [Test Accuracy: 0.740]\n",
      "[Epoch: 61/100]  [Training Loss: 0.015]  [Test Loss: 2.921]  [Test Accuracy: 0.748]\n",
      "[Epoch: 62/100]  [Training Loss: 0.042]  [Test Loss: 2.693]  [Test Accuracy: 0.715]\n",
      "[Epoch: 63/100]  [Training Loss: 0.089]  [Test Loss: 2.389]  [Test Accuracy: 0.713]\n",
      "[Epoch: 64/100]  [Training Loss: 0.076]  [Test Loss: 2.269]  [Test Accuracy: 0.722]\n",
      "[Epoch: 65/100]  [Training Loss: 0.055]  [Test Loss: 2.266]  [Test Accuracy: 0.729]\n",
      "[Epoch: 66/100]  [Training Loss: 0.031]  [Test Loss: 2.479]  [Test Accuracy: 0.731]\n",
      "[Epoch: 67/100]  [Training Loss: 0.020]  [Test Loss: 2.621]  [Test Accuracy: 0.732]\n",
      "[Epoch: 68/100]  [Training Loss: 0.049]  [Test Loss: 2.486]  [Test Accuracy: 0.708]\n",
      "[Epoch: 69/100]  [Training Loss: 0.044]  [Test Loss: 2.189]  [Test Accuracy: 0.735]\n",
      "[Epoch: 70/100]  [Training Loss: 0.045]  [Test Loss: 2.196]  [Test Accuracy: 0.733]\n",
      "[Epoch: 71/100]  [Training Loss: 0.030]  [Test Loss: 2.541]  [Test Accuracy: 0.723]\n",
      "[Epoch: 72/100]  [Training Loss: 0.041]  [Test Loss: 2.266]  [Test Accuracy: 0.735]\n",
      "[Epoch: 73/100]  [Training Loss: 0.025]  [Test Loss: 2.717]  [Test Accuracy: 0.733]\n",
      "[Epoch: 74/100]  [Training Loss: 0.051]  [Test Loss: 2.374]  [Test Accuracy: 0.723]\n",
      "[Epoch: 75/100]  [Training Loss: 0.030]  [Test Loss: 2.558]  [Test Accuracy: 0.735]\n",
      "[Epoch: 76/100]  [Training Loss: 0.011]  [Test Loss: 2.764]  [Test Accuracy: 0.726]\n",
      "[Epoch: 77/100]  [Training Loss: 0.010]  [Test Loss: 2.766]  [Test Accuracy: 0.735]\n",
      "[Epoch: 78/100]  [Training Loss: 0.005]  [Test Loss: 2.833]  [Test Accuracy: 0.727]\n",
      "[Epoch: 79/100]  [Training Loss: 0.005]  [Test Loss: 3.201]  [Test Accuracy: 0.733]\n",
      "[Epoch: 80/100]  [Training Loss: 0.012]  [Test Loss: 3.093]  [Test Accuracy: 0.730]\n",
      "[Epoch: 81/100]  [Training Loss: 0.005]  [Test Loss: 3.225]  [Test Accuracy: 0.734]\n",
      "[Epoch: 82/100]  [Training Loss: 0.008]  [Test Loss: 3.280]  [Test Accuracy: 0.734]\n",
      "[Epoch: 83/100]  [Training Loss: 0.214]  [Test Loss: 1.391]  [Test Accuracy: 0.652]\n",
      "[Epoch: 84/100]  [Training Loss: 0.161]  [Test Loss: 1.826]  [Test Accuracy: 0.722]\n",
      "[Epoch: 85/100]  [Training Loss: 0.040]  [Test Loss: 2.001]  [Test Accuracy: 0.732]\n",
      "[Epoch: 86/100]  [Training Loss: 0.019]  [Test Loss: 2.458]  [Test Accuracy: 0.743]\n",
      "[Epoch: 87/100]  [Training Loss: 0.007]  [Test Loss: 2.771]  [Test Accuracy: 0.734]\n",
      "[Epoch: 88/100]  [Training Loss: 0.003]  [Test Loss: 2.952]  [Test Accuracy: 0.735]\n",
      "[Epoch: 89/100]  [Training Loss: 0.001]  [Test Loss: 3.063]  [Test Accuracy: 0.732]\n",
      "[Epoch: 90/100]  [Training Loss: 0.001]  [Test Loss: 3.180]  [Test Accuracy: 0.736]\n",
      "[Epoch: 91/100]  [Training Loss: 0.001]  [Test Loss: 3.324]  [Test Accuracy: 0.738]\n",
      "[Epoch: 92/100]  [Training Loss: 0.001]  [Test Loss: 3.345]  [Test Accuracy: 0.735]\n",
      "[Epoch: 93/100]  [Training Loss: 0.000]  [Test Loss: 3.505]  [Test Accuracy: 0.738]\n",
      "[Epoch: 94/100]  [Training Loss: 0.000]  [Test Loss: 3.555]  [Test Accuracy: 0.733]\n",
      "[Epoch: 95/100]  [Training Loss: 0.000]  [Test Loss: 3.612]  [Test Accuracy: 0.729]\n",
      "[Epoch: 96/100]  [Training Loss: 0.000]  [Test Loss: 3.607]  [Test Accuracy: 0.738]\n",
      "[Epoch: 97/100]  [Training Loss: 0.000]  [Test Loss: 3.766]  [Test Accuracy: 0.733]\n",
      "[Epoch: 98/100]  [Training Loss: 0.000]  [Test Loss: 3.761]  [Test Accuracy: 0.735]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[Epoch: 99/100]  [Training Loss: 0.000]  [Test Loss: 3.828]  [Test Accuracy: 0.732]\n",
      "[Epoch: 100/100]  [Training Loss: 0.000]  [Test Loss: 3.877]  [Test Accuracy: 0.735]\n"
     ]
    }
   ],
   "source": [
    "epochs = 100\n",
    "steps = 0\n",
    "train_losses, test_losses = [], []\n",
    "for e in range(epochs):\n",
    "    running_loss = 0\n",
    "    net.train()\n",
    "    for images, labels in train_loader:   \n",
    "        if torch.cuda.is_available():\n",
    "            images, labels = images.cuda(), labels.cuda()     \n",
    "        optimizer.zero_grad()\n",
    "        log_ps = net(images)\n",
    "        loss = loss_function(log_ps, labels)\n",
    "        loss.backward()\n",
    "        optimizer.step()        \n",
    "        running_loss += loss.item()        \n",
    "    else:\n",
    "        test_loss = 0\n",
    "        accuracy = 0        \n",
    "        net.eval()\n",
    "        with torch.no_grad():\n",
    "            for images, labels in test_loader:\n",
    "                if torch.cuda.is_available():\n",
    "                    images, labels = images.cuda(), labels.cuda()\n",
    "                log_ps = net(images)\n",
    "                test_loss += loss_function(log_ps, labels)                \n",
    "                # ps = torch.exp(log_ps)\n",
    "                top_p, top_class = log_ps.topk(1, dim=1)\n",
    "                equals = top_class.long() == labels.long().view(*top_class.shape)\n",
    "                accuracy += torch.mean(equals.type(torch.FloatTensor))                \n",
    "        train_losses.append(running_loss/len(train_loader))\n",
    "        test_losses.append(test_loss/len(test_loader))\n",
    "        print(\"[Epoch: {}/{}] \".format(e+1, epochs),\n",
    "              \"[Training Loss: {:.3f}] \".format(running_loss/len(train_loader)),\n",
    "              \"[Test Loss: {:.3f}] \".format(test_loss/len(test_loader)),\n",
    "              \"[Test Accuracy: {:.3f}]\".format(accuracy/len(test_loader)))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
